package com.googlecode.h0v3rk4rt.net;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import java.util.StringTokenizer;

import com.googlecode.h0v3rk4rt.graphics.DrivePanel;
import com.googlecode.h0v3rk4rt.graphics.Multiplayer;
import com.googlecode.h0v3rk4rt.util.GameError;

public class MultiplayerClient
{

	public static final int lapsNeededToWin = 3; //Nilay change this if you want
	public static InetAddress server;
	private static final int TCP_PORT = 40315;
	private static final int UDP_PORT = 40316; //the SERVER's UDP port
	public static final int my_UDP_PORT = 40350; //the CLIENT's UDP port
	private static Socket tcpSocket;
	private static DatagramSocket udpSocket;
	private static ObjectInputStream input;
	private static ObjectOutputStream output;
	public static boolean shutdown;
	public static String id;
	public static boolean begunGame = false;
	private static int idn;

	private static long timeStart;
	
	public static void start(String host)
	{
		try {
			server = InetAddress.getByName(host);
			(new TCP()).start(); //the TCP service will start the UDP service (coder is lazy)
		} catch (UnknownHostException e) {
			shutdown("You typed an invalid IP address. Please try again.");
		}
		
	}

	//This method is called when the game is to start
	private static void beginGame()
	{
		begunGame = true;
		try {
			BufferedReader filein =  new BufferedReader(new FileReader("resource/map.txt"));
			for(int i = 0; i < idn; i++) filein.readLine();
			StringTokenizer st = new StringTokenizer(filein.readLine());
			st.nextToken();
			DrivePanel.xMe[0] = Double.parseDouble(st.nextToken());
			DrivePanel.xMe[1] = Double.parseDouble(st.nextToken());
			DrivePanel.thetaMe = Double.parseDouble(st.nextToken());
			DrivePanel.vMe = 0;
			DrivePanel.timeMe = System.currentTimeMillis();
		} catch (FileNotFoundException e) {
			shutdown("Could not find the text file that needs to go with the map.\n" +
					"The text file should be formatted as follows:\n" +
					"0 [InitialxPos for player 0] [InitialyPos for player 0] [Initial angle for player 0]\n" +
					"0 [InitialxPos for player 1] [InitialyPos for player 1] [Initial angle for player 1]\n");
		} catch(IOException e) {
			shutdown("Map text file improperly formatted.\n" +
					"The text file should be formatted as follows:\n" +
					"0 [InitialxPos for player 0] [InitialyPos for player 0] [Initial angle for player 0]\n" +
					"0 [InitialxPos for player 1] [InitialyPos for player 1] [Initial angle for player 1]\n");
		}
		timeStart = System.currentTimeMillis();
	}

	//Sends 4 double variables and a long ( = my data) correctly
	public static void sendMe(double x0, double x1, double v, double theta, long t)
	{
		if (!begunGame) return;
		DatagramPacket packet = NetworkingUtils.makePacket(new double[] {
				x0, x1, v, theta, Double.longBitsToDouble(t) }, server, UDP_PORT + idn);
		try {
			udpSocket.send(packet);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(DrivePanel.laps >= lapsNeededToWin)
			try {
				output.writeObject("win");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
	}

	//is called when client has received an array of 5 doubles
	public static void received(double[] dArray)
	{
		long t = Double.doubleToRawLongBits(dArray[4]);
		if (t < 0)
		{
			DrivePanel.xMe[0] = dArray[0];
			DrivePanel.xMe[1] = dArray[1];
			DrivePanel.vMe = dArray[2];
			DrivePanel.thetaMe = dArray[3];
			DrivePanel.timeMe = -t;
			return;
		}
		DrivePanel.xThem[0] = dArray[0];
		DrivePanel.xThem[1] = dArray[1];
		DrivePanel.vThem = dArray[2];
		DrivePanel.thetaThem = dArray[3];
		DrivePanel.timeThem = t;
	}

	//Listens for packets via TCP
	private static class TCP extends Thread
	{
		public void run()
		{
			try
			{
				connect();
				while (!shutdown)
				{
					String message = (String) input.readObject();
					if (message.startsWith("shutdown")) shutdown();
					else if (message.startsWith("begin")) beginGame();
					else if (message.startsWith("win"))
						shutdown("Congrats! You won. I always believed in you.\n" +
								"Time taken: " + ((double) System.currentTimeMillis()-timeStart)/1000 + " seconds.");
					else if (message.startsWith("lose")) shutdown("Hahahaha! You lost! n00b");
				}
				System.out.println(id + " shutting down.");
			}
			catch (ConnectException ce)
			{
				shutdown("Could not connect to server.\n" +
						"Please make sure someone started a server and that you typed in its IP address.");
				System.exit(1);
			}
			catch (IOException e)
			{
				e.printStackTrace();
				shutdown();
			}
			catch (ClassNotFoundException e)
			{
				GameError.showErrorDialog(e, "Error fetching ID from server!", null);
			}
		}
	}

	//Listens for packets via UDP
	private static class UDP extends Thread
	{
		public void run()
		{
			DatagramPacket packet = NetworkingUtils.blankPacket();
			System.out.println("UDP on client started.");
			while (!shutdown)
			{
				try
				{
					udpSocket.receive(packet);
					received(NetworkingUtils.getData(packet));
				}
				catch (IOException e)
				{
					if (shutdown) ;
					else
					{
						System.out.println("UDP receiving failed; shutting down");
						e.printStackTrace();
						shutdown();
					}
				}
			}
		}
	}

	//Shuts the client down
	public static void shutdown() { shutdown(null); }
	
	public static void shutdown(String s)
	{
		if(shutdown) return;
		begunGame = false;
		System.out.println("Client " + id + " actually shutting down.");
		shutdown = true;
		if(udpSocket != null) udpSocket.close();
		try {
			if(output != null) output.writeObject("shutdown");
			if(tcpSocket != null) tcpSocket.close();
		} catch (IOException e) {}
		output = null;
		input = null;
		tcpSocket = null;
		udpSocket = null;
		Multiplayer.shutdown(s);
	}

	// Connect to a game server and set up the UDP server
	private static void connect() throws IOException, ClassNotFoundException
	{
		tcpSocket = new Socket(server, TCP_PORT);
		output = new ObjectOutputStream(tcpSocket.getOutputStream());
		input = new ObjectInputStream(tcpSocket.getInputStream());
		output.flush();
		id = (String) input.readObject();
		System.out.println("Fetched ID: " + id);
		idn = id.charAt(6) - '0';
		udpSocket = new DatagramSocket(my_UDP_PORT + idn);
		(new UDP()).start();
		output.writeObject("login");
		output.flush();
	}

}
